package session

import (
	"context"
	"database/sql"
	"gitee.com/zhendliu/sorm/clause"
	"gitee.com/zhendliu/sorm/dialect"
	"gitee.com/zhendliu/sorm/operator"
	"gitee.com/zhendliu/sorm/schema"
	"gitee.com/zhendliu/sorm/server"
	"gitee.com/zhendliu/sorm/spliter"
	"strings"
)

// Session keep a pointer to sql.DB and provides all execution of all
// kind of database operations.
type Session struct {
	db          *server.DB // 数据库连接
	dbs         map[string]*server.DB
	isTx        bool
	txs         []*sql.Tx
	txMap       map[*sql.DB]*sql.Tx
	ctx         context.Context           // 上下文
	refTable    *schema.Schema            // 存储表的结构体
	clause      clause.Clause             // 存储sql语句的结构体
	tableClause map[string]clause.Clause  // 存储多表sql语句的结构体
	sql         strings.Builder           // sql语句
	sqlVars     []interface{}             // sql语句中的参数
	selectField []string                  // 需要查询的字段
	omitField   []string                  // 不需要查询的字段
	spliter     *map[string]spliter.Split // 数据库分表的信息 key为表名 value为分表的信息
	operator    operator.Operator
	config      *server.Config
}

// New creates  instance of Session
func New(db *server.DB, split *map[string]spliter.Split, dbs map[string]*server.DB, opt operator.Operator, config *server.Config) *Session {
	return &Session{
		db:          db,
		spliter:     split,
		tableClause: map[string]clause.Clause{},
		dbs:         dbs,
		txMap:       map[*sql.DB]*sql.Tx{},
		operator:    opt,
		ctx:         context.Background(),
		config:      config,
	}
}

// Clear initialize the state of a session
func (s *Session) Clear() {
	s.ctx = context.Background()
	s.refTable = nil
	s.clause = clause.Clause{}
	s.tableClause = make(map[string]clause.Clause)
	s.sql.Reset()
	s.sqlVars = nil
	s.selectField = nil
	s.omitField = nil

}

// Exec raw sql with sqlVars
func (s *Session) exec(ctx context.Context, db *sql.DB, dia dialect.Dialect, sqlInfo string, args []interface{}) (result sql.Result, err error) {
	if s.isTx {
		var (
			tx *sql.Tx
			ok bool
		)
		if tx, ok = s.txMap[db]; !ok {
			tx, err = db.Begin()
			if err != nil {
				return nil, err
			}
			s.txs = append(s.txs, tx)
			s.txMap[db] = tx
		}

		/*if err != nil {
			return nil, err
		}*/
		return s.operator.ExecTx(ctx, tx, dia, sqlInfo, args)
	} else {
		return s.operator.Exec(ctx, db, dia, sqlInfo, args)
	}
}

// Query QueryRow gets a record from db
func (s *Session) Query(ctx context.Context, db *sql.DB, dia dialect.Dialect, sqlInfo string, args []interface{}, list interface{}) error {
	return s.operator.Query(ctx, db, dia, sqlInfo, args, list, 0)
}

// QueryPagination QueryRow gets a record from db
func (s *Session) QueryPagination(ctx context.Context, db *sql.DB, dia dialect.Dialect, sqlInfo string, args []interface{}, limit, offset int, list interface{}) (err error) {
	return s.operator.QueryPagination(ctx, db, dia, sqlInfo, args, limit, offset, list)
}

func (s *Session) QueryRowContext(ctx context.Context, db *sql.DB, dia dialect.Dialect, sqlInfo string, args []interface{}) *sql.Row {
	return s.operator.QueryRowContext(ctx, db, dia, sqlInfo, args)
}

// Raw appends sql and sqlVars
func (s *Session) Raw(sql string, values ...interface{}) *Session {
	s.sql.WriteString(sql)
	s.sql.WriteString(" ")
	s.sqlVars = append(s.sqlVars, values...)
	return s
}
